/*
 * JavaScript Assembler.
 * Copyright (c) 1998 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 */

/*
 * $Source: /usr/local/cvsroot/ngs/js/jsc/asm.js,v $
 * $Id: asm.js,v 1.15 1998/10/26 15:25:21 mtr Exp $
 */

/* Byte-code file definitions. */

JSC$BC_MAGIC = 0xc0014a53;

JSC$BC_SECT_CODE = 0;
JSC$BC_SECT_CONSTANTS = 1;
JSC$BC_SECT_SYMTAB = 2;
JSC$BC_SECT_DEBUG = 3;

JSC$CONST_INT = 3;
JSC$CONST_STRING = 4;
JSC$CONST_FLOAT = 5;
JSC$CONST_SYMBOL = 10;
JSC$CONST_REGEXP = 11;
JSC$CONST_NAN = 13;

JSC$CONST_REGEXP_FLAG_G = 0x01;
JSC$CONST_REGEXP_FLAG_I = 0x02;

JSC$DEBUG_FILENAME = 1;
JSC$DEBUG_LINENUMBER = 2;

/* Opcode definitions. */

JSC$OP_HALT = 0;
JSC$OP_DONE = 1;
JSC$OP_NOP = 2;
JSC$OP_DUP = 3;
JSC$OP_POP = 4;
JSC$OP_POP_N = 5;
JSC$OP_APOP = 6;
JSC$OP_SWAP = 7;
JSC$OP_ROLL = 8;
JSC$OP_CONST = 9;
JSC$OP_CONST_NULL = 10;
JSC$OP_CONST_TRUE = 11;
JSC$OP_CONST_FALSE = 12;
JSC$OP_CONST_UNDEFINED = 13;
JSC$OP_CONST_I0 = 14;
JSC$OP_CONST_I1 = 15;
JSC$OP_CONST_I2 = 16;
JSC$OP_CONST_I3 = 17;
JSC$OP_CONST_I = 18;
JSC$OP_LOAD_GLOBAL = 19;
JSC$OP_STORE_GLOBAL = 20;
JSC$OP_LOAD_ARG = 21;
JSC$OP_STORE_ARG = 22;
JSC$OP_LOAD_LOCAL = 23;
JSC$OP_STORE_LOCAL = 24;
JSC$OP_LOAD_PROPERTY = 25;
JSC$OP_STORE_PROPERTY = 26;
JSC$OP_LOAD_ARRAY = 27;
JSC$OP_STORE_ARRAY = 28;
JSC$OP_NTH = 29;
JSC$OP_CMP_EQ = 30;
JSC$OP_CMP_NE = 31;
JSC$OP_CMP_LT = 32;
JSC$OP_CMP_GT = 33;
JSC$OP_CMP_LE = 34;
JSC$OP_CMP_GE = 35;
JSC$OP_CMP_SEQ = 36;
JSC$OP_CMP_SNE = 37;
JSC$OP_SUB = 38;
JSC$OP_ADD = 39;
JSC$OP_MUL = 40;
JSC$OP_DIV = 41;
JSC$OP_MOD = 42;
JSC$OP_NEG = 43;
JSC$OP_AND = 44;
JSC$OP_NOT = 45;
JSC$OP_OR = 46;
JSC$OP_XOR = 47;
JSC$OP_SHIFT_LEFT = 48;
JSC$OP_SHIFT_RIGHT = 49;
JSC$OP_SHIFT_RRIGHT = 50;
JSC$OP_IFFALSE = 51;
JSC$OP_IFTRUE = 52;
JSC$OP_CALL_METHOD = 53;
JSC$OP_JMP = 54;
JSC$OP_JSR = 55;
JSC$OP_RETURN = 56;
JSC$OP_TYPEOF = 57;
JSC$OP_NEW = 58;
JSC$OP_DELETE_PROPERTY = 59;
JSC$OP_DELETE_ARRAY = 60;
JSC$OP_LOCALS = 61;
JSC$OP_MIN_ARGS = 62;
JSC$OP_LOAD_NTH_ARG = 63;
JSC$OP_WITH_PUSH = 64;
JSC$OP_WITH_POP = 65;
JSC$OP_TRY_PUSH = 66;
JSC$OP_TRY_POP = 67;
JSC$OP_THROW = 68;

/* Type aware operands. */
JSC$OP_IFFALSE_B = 69;
JSC$OP_IFTRUE_B = 70;
JSC$OP_ADD_1_I = 71;
JSC$OP_ADD_2_I = 72;
JSC$OP_LOAD_GLOBAL_W = 73;
JSC$OP_JSR_W = 74;

/* Internal values. */
JSC$ASM_SYMBOL = 1000;
JSC$ASM_LABEL = 1001;

/*
 * General helpers.
 */

/* Generate byte-code for operands with Int8 value. */
function JSC$ASM_bytecode_int8()
{
	return String.pack("C", this.value);
}

/* Generate byte-code for operands with Int16 value. */
function JSC$ASM_bytecode_int16()
{
	return String.pack("n", this.value);
}

/* Generate byte-code for operands with Int32 value. */
function JSC$ASM_bytecode_int32()
{
	return String.pack("N", this.value);
}

/* Generate byte-code for operands with Symbol value. */
function JSC$ASM_bytecode_symbol()
{
	var cid = JSC$asm_genconstant(String.pack("C", JSC$CONST_SYMBOL)
								  + this.value + String.pack("C", 0));
	return String.pack("N", cid);
}

/* Generate byte-code for local jump operands. */
function JSC$ASM_bytecode_local_jump()
{
	var delta = this.value.offset - (this.offset + this.size);
	return String.pack("N", delta);
}


/*
 * Assembler operands.
 */

/* Symbol. */

function JSC$ASM_symbol(ln, value)
{
	this.type = JSC$ASM_SYMBOL;
	this.linenum = ln;
	this.value = value;
	this.size = 0;
	this.print = JSC$ASM_symbol_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_symbol_print(stream)
{
	stream.write("\n" + this.value + ":\n");
}

/* Label */

function JSC$ASM_label()
{
	this.type = JSC$ASM_LABEL;
	this.linenum = 0;
	this.size = 0;
	this.value = JSC$asm_label_count++;
	this.referenced = false;
	this.next = null;
	this.print = JSC$ASM_label_print;
	this.link = JSC$asm_link;
	this.format = JSC$ASM_label_format;
}

function JSC$ASM_label_print(stream)
{
	stream.write(this.format() + ":\n");
}

function JSC$ASM_label_format()
{
	return ".L" + this.value.toString();
}

/* halt */

function JSC$ASM_halt(ln)
{
	this.type = JSC$OP_HALT;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_halt_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_halt_print(stream)
{
	stream.write("\thalt\n");
}

/* done */

function JSC$ASM_done(ln)
{
	this.type = JSC$OP_DONE;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_done_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_done_print(stream)
{
	stream.write("\tdone\n");
}

/* nop */

function JSC$ASM_nop(ln)
{
	this.type = JSC$OP_NOP;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_nop_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_nop_print(stream)
{
	stream.write("\tnop\n");
}

/* dup */

function JSC$ASM_dup(ln)
{
	this.type = JSC$OP_DUP;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_dup_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_dup_print(stream)
{
	stream.write("\tdup\n");
}

/* pop */

function JSC$ASM_pop(ln)
{
	this.type = JSC$OP_POP;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_pop_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_pop_print(stream)
{
	stream.write("\tpop\n");
}

/* pop_n */

function JSC$ASM_pop_n(ln, value)
{
	this.type = JSC$OP_POP_N;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -value;
	this.size = 2;
	this.print = JSC$ASM_pop_n_print;
	this.link = JSC$asm_link;
	this.bytecode = JSC$ASM_bytecode_int8;
}

function JSC$ASM_pop_n_print(stream)
{
	stream.write("\tpop_n\t\t" + this.value.toString() + "\n");
}

/* apop */

function JSC$ASM_apop(ln, value)
{
	this.type = JSC$OP_APOP;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -value;
	this.size = 2;
	this.print = JSC$ASM_apop_print;
	this.link = JSC$asm_link;
	this.bytecode = JSC$ASM_bytecode_int8;
}

function JSC$ASM_apop_print(stream)
{
	stream.write("\tapop\t\t" + this.value.toString() + "\n");
}

/* swap */

function JSC$ASM_swap(ln)
{
	this.type = JSC$OP_SWAP;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_swap_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_swap_print(stream)
{
	stream.write("\tswap\n");
}

/* roll */
function JSC$ASM_roll(ln, value)
{
	this.type = JSC$OP_ROLL;
	this.linenum = ln;
	this.value = value;
	this.size = 2;
	this.print = JSC$ASM_roll_print;
	this.link = JSC$asm_link;
	this.bytecode = JSC$ASM_bytecode_int8;
}

function JSC$ASM_roll_print(stream)
{
	stream.write("\troll\t\t" + this.value.toString() + "\n");
}

/* const */

function JSC$ASM_const(ln, value)
{
	this.type = JSC$OP_CONST;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 5;
	this.print = JSC$ASM_const_print;
	this.link = JSC$asm_link;
	this.bytecode = JSC$ASM_const_bytecode;
}

function JSC$ASM_const_print(stream)
{
	if (typeof this.value == "number")
		stream.write("\tconst\t\t" + this.value.toString() + "\n");
	else if (typeof this.value == "string" || typeof this.value == "#builtin") {
		var i, c;
		var ender, src;
		var stringp = (typeof this.value == "string");

		if (stringp) {
			ender = "\"";
			src = this.value;
		} else {
			ender = "/";
			src = this.value.source;
		}

		stream.write("\tconst\t\t" + ender);
		for (i = 0; i < src.length; i++) {
			c = src.charCodeAt(i);
			if (c == ender[0] || c == '\\'.charCodeAt(0))
				stream.write("\\" + src.charAt(i));
			else if (c == '\n'.charCodeAt(0))
				stream.write("\\n");
			else if (c == '\r'.charCodeAt(0))
				stream.write("\\r");
			else if (c == '\t'.charCodeAt(0))
				stream.write("\\t");
			else if (c == '\f'.charCodeAt(0))
				stream.write("\\f");
			else
				stream.write(src.charAt(i));
		}
		stream.write(ender);

		if (!stringp) {
			if (this.value.global)
				stream.write("g");
			if (this.value.ignoreCase)
				stream.write("i");
		}

		stream.write("\n");
	}
}

function JSC$ASM_const_bytecode()
{
	var cid;

	if (typeof this.value == "number") {
		if (isInt(this.value))
			cid = JSC$asm_genconstant(String.pack("CN", JSC$CONST_INT, this.value));
		else if (isFloat(this.value))
			cid = JSC$asm_genconstant(String.pack("Cd", JSC$CONST_FLOAT, this.value));
		else
			cid = JSC$asm_genconstant(String.pack("C", JSC$CONST_NAN));
	} else if (typeof this.value == "string")
		cid = JSC$asm_genconstant(String.pack("CN", JSC$CONST_STRING, this.value.length)
								  + this.value);
	else if (typeof this.value == "#builtin") {
		/* Regular expression. */
		var flags = 0;

		if (this.value.global)
			flags |= JSC$CONST_REGEXP_FLAG_G;
		if (this.value.ignoreCase)
			flags |= JSC$CONST_REGEXP_FLAG_I;

		cid =
			JSC$asm_genconstant(String.
								pack("CCN", JSC$CONST_REGEXP, flags, this.value.source.length)
								+ this.value.source);
	} else
		error("ASM_const_bytecode(): unknown type: " + typeof this.value);

	return String.pack("N", cid);
}

/* const_null */

function JSC$ASM_const_null(ln)
{
	this.type = JSC$OP_CONST_NULL;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_null_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_null_print(stream)
{
	stream.write("\tconst_null\n");
}

/* const_true */

function JSC$ASM_const_true(ln)
{
	this.type = JSC$OP_CONST_TRUE;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_true_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_true_print(stream)
{
	stream.write("\tconst_true\n");
}

/* const_false */

function JSC$ASM_const_false(ln)
{
	this.type = JSC$OP_CONST_FALSE;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_false_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_false_print(stream)
{
	stream.write("\tconst_false\n");
}

/* const_undefined */

function JSC$ASM_const_undefined(ln)
{
	this.type = JSC$OP_CONST_UNDEFINED;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_undefined_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_undefined_print(stream)
{
	stream.write("\tconst_undefined\n");
}

/* const_i0 */

function JSC$ASM_const_i0(ln)
{
	this.type = JSC$OP_CONST_I0;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_i0_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_i0_print(stream)
{
	stream.write("\tconst_i0\n");
}

/* const_i1 */

function JSC$ASM_const_i1(ln)
{
	this.type = JSC$OP_CONST_I1;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_i1_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_i1_print(stream)
{
	stream.write("\tconst_i1\n");
}

/* const_i2 */

function JSC$ASM_const_i2(ln)
{
	this.type = JSC$OP_CONST_I2;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_i2_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_i2_print(stream)
{
	stream.write("\tconst_i2\n");
}

/* const_i3 */

function JSC$ASM_const_i3(ln)
{
	this.type = JSC$OP_CONST_I3;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_const_i3_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_i3_print(stream)
{
	stream.write("\tconst_i3\n");
}

/* const_i */

function JSC$ASM_const_i(ln, value)
{
	this.type = JSC$OP_CONST_I;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 5;
	this.print = JSC$ASM_const_i_print;
	this.bytecode = JSC$ASM_bytecode_int32;
	this.link = JSC$asm_link;
}

function JSC$ASM_const_i_print(stream)
{
	stream.write("\tconst_i\t\t" + this.value.toString() + "\n");
}

/* load_global */

function JSC$ASM_load_global(ln, value)
{
	this.type = JSC$OP_LOAD_GLOBAL;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 5;
	this.print = JSC$ASM_load_global_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_global_print(stream)
{
	stream.write("\tload_global\t" + this.value + "\n");
}

/* store_global */

function JSC$ASM_store_global(ln, value)
{
	this.type = JSC$OP_STORE_GLOBAL;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 5;
	this.print = JSC$ASM_store_global_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_store_global_print(stream)
{
	stream.write("\tstore_global\t" + this.value + "\n");
}

/* load_arg */

function JSC$ASM_load_arg(ln, value)
{
	this.type = JSC$OP_LOAD_ARG;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 2;
	this.print = JSC$ASM_load_arg_print;
	this.bytecode = JSC$ASM_bytecode_int8;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_arg_print(stream)
{
	stream.write("\tload_arg\t" + this.value.toString() + "\n");
}

/* store_arg */

function JSC$ASM_store_arg(ln, value)
{
	this.type = JSC$OP_STORE_ARG;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 2;
	this.print = JSC$ASM_store_arg_print;
	this.bytecode = JSC$ASM_bytecode_int8;
	this.link = JSC$asm_link;
}

function JSC$ASM_store_arg_print(stream)
{
	stream.write("\tstore_arg\t" + this.value.toString() + "\n");
}

/* load_local */

function JSC$ASM_load_local(ln, value)
{
	this.type = JSC$OP_LOAD_LOCAL;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 3;
	this.print = JSC$ASM_load_local_print;
	this.bytecode = JSC$ASM_bytecode_int16;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_local_print(stream)
{
	stream.write("\tload_local\t" + this.value.toString() + "\n");
}

/* store_local */

function JSC$ASM_store_local(ln, value)
{
	this.type = JSC$OP_STORE_LOCAL;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 3;
	this.print = JSC$ASM_store_local_print;
	this.bytecode = JSC$ASM_bytecode_int16;
	this.link = JSC$asm_link;
}

function JSC$ASM_store_local_print(stream)
{
	stream.write("\tstore_local\t" + this.value.toString() + "\n");
}

/* load_property */

function JSC$ASM_load_property(ln, value)
{
	this.type = JSC$OP_LOAD_PROPERTY;
	this.linenum = ln;
	this.value = value;
	this.size = 5;
	this.print = JSC$ASM_load_property_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_property_print(stream)
{
	stream.write("\tload_property\t" + this.value + "\n");
}

/* store_property */

function JSC$ASM_store_property(ln, value)
{
	this.type = JSC$OP_STORE_PROPERTY;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -2;
	this.size = 5;
	this.print = JSC$ASM_store_property_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_store_property_print(stream)
{
	stream.write("\tstore_property\t" + this.value + "\n");
}

/* load_array */

function JSC$ASM_load_array(ln)
{
	this.type = JSC$OP_LOAD_ARRAY;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_load_array_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_array_print(stream)
{
	stream.write("\tload_array\n");
}

/* store_array */

function JSC$ASM_store_array(ln)
{
	this.type = JSC$OP_STORE_ARRAY;
	this.linenum = ln;
	this.stack_delta = -3;
	this.size = 1;
	this.print = JSC$ASM_store_array_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_store_array_print(stream)
{
	stream.write("\tstore_array\n");
}

/* nth */

function JSC$ASM_nth(ln)
{
	this.type = JSC$OP_NTH;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_nth_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_nth_print(stream)
{
	stream.write("\tnth\n");
}

/* cmp_eq */

function JSC$ASM_cmp_eq(ln)
{
	this.type = JSC$OP_CMP_EQ;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_eq_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_eq_print(stream)
{
	stream.write("\tcmp_eq\n");
}

/* cmp_ne */

function JSC$ASM_cmp_ne(ln)
{
	this.type = JSC$OP_CMP_NE;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_ne_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_ne_print(stream)
{
	stream.write("\tcmp_ne\n");
}

/* cmp_lt */

function JSC$ASM_cmp_lt(ln)
{
	this.type = JSC$OP_CMP_LT;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_lt_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_lt_print(stream)
{
	stream.write("\tcmp_lt\n");
}

/* cmp_gt */

function JSC$ASM_cmp_gt(ln)
{
	this.type = JSC$OP_CMP_GT;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_gt_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_gt_print(stream)
{
	stream.write("\tcmp_gt\n");
}

/* cmp_le */

function JSC$ASM_cmp_le(ln)
{
	this.type = JSC$OP_CMP_LE;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_le_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_le_print(stream)
{
	stream.write("\tcmp_le\n");
}

/* cmp_ge */

function JSC$ASM_cmp_ge(ln)
{
	this.type = JSC$OP_CMP_GE;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_ge_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_ge_print(stream)
{
	stream.write("\tcmp_ge\n");
}

/* cmp_seq */

function JSC$ASM_cmp_seq(ln)
{
	this.type = JSC$OP_CMP_SEQ;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_seq_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_seq_print(stream)
{
	stream.write("\tcmp_seq\n");
}

/* cmp_sne */

function JSC$ASM_cmp_sne(ln)
{
	this.type = JSC$OP_CMP_SNE;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_cmp_sne_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_cmp_sne_print(stream)
{
	stream.write("\tcmp_sne\n");
}

/* sub */

function JSC$ASM_sub(ln)
{
	this.type = JSC$OP_SUB;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_sub_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_sub_print(stream)
{
	stream.write("\tsub\n");
}

/* add */

function JSC$ASM_add(ln)
{
	this.type = JSC$OP_ADD;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_add_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_add_print(stream)
{
	stream.write("\tadd\n");
}

/* mul */

function JSC$ASM_mul(ln)
{
	this.type = JSC$OP_MUL;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_mul_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_mul_print(stream)
{
	stream.write("\tmul\n");
}

/* div */

function JSC$ASM_div(ln)
{
	this.type = JSC$OP_DIV;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_div_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_div_print(stream)
{
	stream.write("\tdiv\n");
}

/* mod */

function JSC$ASM_mod(ln)
{
	this.type = JSC$OP_MOD;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_mod_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_mod_print(stream)
{
	stream.write("\tmod\n");
}

/* neg */

function JSC$ASM_neg(ln)
{
	this.type = JSC$OP_NEG;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_neg_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_neg_print(stream)
{
	stream.write("\tneg\n");
}

/* and */

function JSC$ASM_and(ln)
{
	this.type = JSC$OP_AND;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_and_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_and_print(stream)
{
	stream.write("\tand\n");
}

/* not */

function JSC$ASM_not(ln)
{
	this.type = JSC$OP_NOT;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_not_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_not_print(stream)
{
	stream.write("\tnot\n");
}

/* or */

function JSC$ASM_or(ln)
{
	this.type = JSC$OP_OR;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_or_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_or_print(stream)
{
	stream.write("\tor\n");
}

/* xor */

function JSC$ASM_xor(ln)
{
	this.type = JSC$OP_XOR;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_xor_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_xor_print(stream)
{
	stream.write("\txor\n");
}

/* shift_left */

function JSC$ASM_shift_left(ln)
{
	this.type = JSC$OP_SHIFT_LEFT;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_shift_left_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_shift_left_print(stream)
{
	stream.write("\tshift_left\n");
}

/* shift_right */

function JSC$ASM_shift_right(ln)
{
	this.type = JSC$OP_SHIFT_RIGHT;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_shift_right_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_shift_right_print(stream)
{
	stream.write("\tshift_right\n");
}

/* shift_rright */

function JSC$ASM_shift_rright(ln)
{
	this.type = JSC$OP_SHIFT_RRIGHT;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_shift_rright_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_shift_rright_print(stream)
{
	stream.write("\tshift_rright\n");
}

/* iffalse */

function JSC$ASM_iffalse(ln, value)
{
	this.type = JSC$OP_IFFALSE;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 5;
	this.print = JSC$ASM_iffalse_print;
	this.bytecode = JSC$ASM_bytecode_local_jump;
	this.link = JSC$asm_link;
}

function JSC$ASM_iffalse_print(stream)
{
	stream.write("\tiffalse\t\t" + this.value.format() + "\n");
}

/* iftrue */

function JSC$ASM_iftrue(ln, value)
{
	this.type = JSC$OP_IFTRUE;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 5;
	this.print = JSC$ASM_iftrue_print;
	this.bytecode = JSC$ASM_bytecode_local_jump;
	this.link = JSC$asm_link;
}

function JSC$ASM_iftrue_print(stream)
{
	stream.write("\tiftrue\t\t" + this.value.format() + "\n");
}

/* call_method */

function JSC$ASM_call_method(ln, value)
{
	this.type = JSC$OP_CALL_METHOD;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 5;
	this.print = JSC$ASM_call_method_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_call_method_print(stream)
{
	stream.write("\tcall_method\t" + this.value + "\n");
}

/* jmp */

function JSC$ASM_jmp(ln, value)
{
	this.type = JSC$OP_JMP;
	this.linenum = ln;
	this.value = value;
	this.size = 5;
	this.print = JSC$ASM_jmp_print;
	this.bytecode = JSC$ASM_bytecode_local_jump;
	this.link = JSC$asm_link;
}

function JSC$ASM_jmp_print(stream)
{
	stream.write("\tjmp\t\t" + this.value.format() + "\n");
}

/* jsr */

function JSC$ASM_jsr(ln)
{
	this.type = JSC$OP_JSR;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_jsr_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_jsr_print(stream)
{
	stream.write("\tjsr\n");
}

/* return */

function JSC$ASM_return(ln)
{
	this.type = JSC$OP_RETURN;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_return_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_return_print(stream)
{
	stream.write("\treturn\n");
}

/* typeof */

function JSC$ASM_typeof(ln)
{
	this.type = JSC$OP_TYPEOF;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_typeof_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_typeof_print(stream)
{
	stream.write("\ttypeof\n");
}

/* new */

function JSC$ASM_new(ln)
{
	this.type = JSC$OP_NEW;
	this.linenum = ln;
	this.stack_delta = 1;
	this.size = 1;
	this.print = JSC$ASM_new_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_new_print(stream)
{
	stream.write("\tnew\n");
}

/* delete_property */

function JSC$ASM_delete_property(ln, value)
{
	this.type = JSC$OP_DELETE_PROPERTY;
	this.linenum = ln;
	this.value = value;
	this.size = 5;
	this.print = JSC$ASM_delete_property_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_delete_property_print(stream)
{
	stream.write("\tdelete_property\t" + this.value + "\n");
}

/* delete_array */

function JSC$ASM_delete_array(ln)
{
	this.type = JSC$OP_DELETE_ARRAY;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_delete_array_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_delete_array_print(stream)
{
	stream.write("\tdelete_array\n");
}

/* locals */

function JSC$ASM_locals(ln, value)
{
	this.type = JSC$OP_LOCALS;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = value;
	this.size = 3;
	this.print = JSC$ASM_locals_print;
	this.bytecode = JSC$ASM_bytecode_int16;
	this.link = JSC$asm_link;
}

function JSC$ASM_locals_print(stream)
{
	stream.write("\tlocals\t\t" + this.value.toString() + "\n");
}

/* min_args */

function JSC$ASM_min_args(ln, value)
{
	this.type = JSC$OP_MIN_ARGS;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 2;
	this.print = JSC$ASM_min_args_print;
	this.bytecode = JSC$ASM_bytecode_int8;
	this.link = JSC$asm_link;
}

function JSC$ASM_min_args_print(stream)
{
	stream.write("\tmin_args\t" + this.value.toString() + "\n");
}

/* load_nth_arg */

function JSC$ASM_load_nth_arg(ln)
{
	this.type = JSC$OP_LOAD_NTH_ARG;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_load_nth_arg_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_nth_arg_print(stream)
{
	stream.write("\tload_nth_arg\n");
}

/* with_push */

function JSC$ASM_with_push(ln)
{
	this.type = JSC$OP_WITH_PUSH;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_with_push_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_with_push_print(stream)
{
	stream.write("\twith_push\n");
}

/* with_pop */

function JSC$ASM_with_pop(ln, value)
{
	this.type = JSC$OP_WITH_POP;
	this.linenum = ln;
	this.value = value;
	this.size = 2;
	this.print = JSC$ASM_with_pop_print;
	this.bytecode = JSC$ASM_bytecode_int8;
	this.link = JSC$asm_link;
}

function JSC$ASM_with_pop_print(stream)
{
	stream.write("\twith_pop\t" + this.value.toString() + "\n");
}

/* try_push */

function JSC$ASM_try_push(ln, value)
{
	this.type = JSC$OP_TRY_PUSH;
	this.linenum = ln;
	this.value = value;
	this.size = 5;
	this.print = JSC$ASM_try_push_print;
	this.bytecode = JSC$ASM_bytecode_local_jump;
	this.link = JSC$asm_link;
}

function JSC$ASM_try_push_print(stream)
{
	stream.write("\ttry_push\t" + this.value.format() + "\n");
}

/* try_pop */

function JSC$ASM_try_pop(ln, value)
{
	this.type = JSC$OP_TRY_POP;
	this.linenum = ln;
	this.value = value;
	this.size = 2;
	this.print = JSC$ASM_try_pop_print;
	this.bytecode = JSC$ASM_bytecode_int8;
	this.link = JSC$asm_link;
}

function JSC$ASM_try_pop_print(stream)
{
	stream.write("\ttry_pop\t\t" + this.value.toString() + "\n");
}

/* throw */

function JSC$ASM_throw(ln)
{
	this.type = JSC$OP_THROW;
	this.linenum = ln;
	this.stack_delta = -1;
	this.size = 1;
	this.print = JSC$ASM_throw_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_throw_print(stream)
{
	stream.write("\tthrow\n");
}

/* iffalse_b */

function JSC$ASM_iffalse_b(ln, value)
{
	this.type = JSC$OP_IFFALSE_B;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 5;
	this.print = JSC$ASM_iffalse_b_print;
	this.bytecode = JSC$ASM_bytecode_local_jump;
	this.link = JSC$asm_link;
}

function JSC$ASM_iffalse_b_print(stream)
{
	stream.write("\tiffalse_b\t" + this.value.format() + "\n");
}

/* iftrue */

function JSC$ASM_iftrue_b(ln, value)
{
	this.type = JSC$OP_IFTRUE_B;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = -1;
	this.size = 5;
	this.print = JSC$ASM_iftrue_b_print;
	this.bytecode = JSC$ASM_bytecode_local_jump;
	this.link = JSC$asm_link;
}

function JSC$ASM_iftrue_b_print(stream)
{
	stream.write("\tiftrue_b\t" + this.value.format() + "\n");
}

/* add_1_i */

function JSC$ASM_add_1_i(ln)
{
	this.type = JSC$OP_ADD_1_I;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_add_1_i_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_add_1_i_print(stream)
{
	stream.write("\tadd_1_i\n");
}

/* add_2_i */

function JSC$ASM_add_2_i(ln)
{
	this.type = JSC$OP_ADD_2_I;
	this.linenum = ln;
	this.size = 1;
	this.print = JSC$ASM_add_2_i_print;
	this.link = JSC$asm_link;
}

function JSC$ASM_add_2_i_print(stream)
{
	stream.write("\tadd_2_i\n");
}

/* load_global_w */

function JSC$ASM_load_global_w(ln, value)
{
	this.type = JSC$OP_LOAD_GLOBAL_W;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 5;
	this.print = JSC$ASM_load_global_w_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_load_global_w_print(stream)
{
	stream.write("\tload_global_w\t" + this.value + "\n");
}

/* jsr_w */

function JSC$ASM_jsr_w(ln, value)
{
	this.type = JSC$OP_JSR_W;
	this.linenum = ln;
	this.value = value;
	this.stack_delta = 1;
	this.size = 5;
	this.print = JSC$ASM_jsr_w_print;
	this.bytecode = JSC$ASM_bytecode_symbol;
	this.link = JSC$asm_link;
}

function JSC$ASM_jsr_w_print(stream)
{
	stream.write("\tjsr_w\t\t" + this.value + "\n");
}

/*
 * General helpers.
 */

function JSC$asm_link()
{
	this.next = null;

	if (JSC$asm_tail != null) {
		JSC$asm_tail_prev = JSC$asm_tail;
		JSC$asm_tail.next = this;
	} else
		JSC$asm_head = this;

	JSC$asm_tail = this;
}


/*
 * The phases of the assembler.
 */

/* This is called from the compiler initialization code. */
function JSC$asm_reset()
{
	JSC$asm_label_count = 1;
	JSC$asm_head = JSC$asm_tail = JSC$asm_tail_prev = null;
	JSC$asm_constcount = 0;
	JSC$asm_constants = null;
	JSC$asm_known_constants = null;
}


function JSC$asm_generate()
{
	var i;

	if (JSC$verbose)
		JSC$message("jsc: generating assembler");

	JSC$ns = new JSC$NameSpace();

	/* Functions. */
	for (i = 0; i < JSC$functions.length; i++)
		JSC$functions[i].asm();

	/* Global statements. */
	if (JSC$global_stmts.length > 0) {
		/* Define the `.global' symbol. */
		new JSC$ASM_symbol(JSC$global_stmts[0].linenum, ".global").link();

		/* Handle local variables. */
		var num_locals = JSC$count_locals_from_stmt_list(JSC$global_stmts);
		if (num_locals > 0)
			new JSC$ASM_locals(JSC$global_stmts[0].linenum, num_locals).link();

		/* Generate assembler. */
		for (i = 0; i < JSC$global_stmts.length; i++)
			JSC$global_stmts[i].asm();

		/*
		 * Fix things so that also the global statement returns something
		 * (this is required when we use eval() in JavaScript).
		 */
		if (JSC$asm_tail_prev == null) {
			/* This is probably illegal, but we don't panic. */
			new JSC$ASM_const_undefined(0).link();
		} else {
			/*
			 * If the latest op is `pop', remove it.  Otherwise, append
			 * a `const_undefined'.
			 */
			if (JSC$asm_tail.type == JSC$OP_POP) {
				JSC$asm_tail = JSC$asm_tail_prev;
				JSC$asm_tail.next = null;
				JSC$asm_tail_prev = null;
			} else
				new JSC$ASM_const_undefined(JSC$asm_tail.linenum).link();
		}
	}

	JSC$ns = null;
}


function JSC$asm_print(src_stream, stream)
{
	var i;
	var last_ln;
	var annotate = src_stream ? true : false;

	if (annotate) {
		stream.write("; -*- asm -*-\n");

		/* Set the prev properties. */
		var prev = null;
		for (i = JSC$asm_head; i != null; prev = i, i = i.next)
			i.prev = prev;

		/*
		 * Fix the label line numbers to be the same that the next
		 * assembler operand has.
		 */
		last_ln = 0;
		for (i = JSC$asm_tail; i != null; i = i.prev) {
			if (i.type == JSC$ASM_LABEL)
				i.linenum = last_ln;
			else if (typeof i.linenum != "undefined")
				last_ln = i.linenum;
		}
	}

	last_ln = 0;
	for (i = JSC$asm_head; i != null; i = i.next) {
		if (typeof i.linenum == "undefined") {
			if (annotate)
				stream.write("; undefined linenum\n");
		} else
			while (annotate && i.linenum > last_ln) {
				var line = src_stream.readln();
				stream.write("; " + line + "\n");
				last_ln++;
			}

		i.print(stream);
	}
}


function JSC$asm_is_load_op(op)
{
	return (op.type == JSC$OP_LOAD_GLOBAL || op.type == JSC$OP_LOAD_ARG
			|| op.type == JSC$OP_LOAD_LOCAL);
}


function JSC$asm_is_store_op(op)
{
	return (op.type == JSC$OP_STORE_GLOBAL || op.type == JSC$OP_STORE_ARG
			|| op.type == JSC$OP_STORE_LOCAL);
}


function JSC$asm_is_local_jump(op)
{
	return (op.type == JSC$OP_JMP || op.type == JSC$OP_IFFALSE || op.type == JSC$OP_IFTRUE
			|| op.type == JSC$OP_IFFALSE_B || op.type == JSC$OP_IFTRUE_B
			|| op.type == JSC$OP_TRY_PUSH);
}


function JSC$asm_is_const_op(op)
{
	return (JSC$OP_CONST <= op.type && op.type <= JSC$OP_CONST_I3);
}


function JSC$asm_lookup_next_op(item)
{
	while (item != null && (item.type == JSC$ASM_LABEL || item.type == JSC$ASM_SYMBOL))
		item = item.next;

	return item;
}


function JSC$asm_optimize(flags)
{
	var item;

	/* Simple peephole optimization. */
	if ((flags & JSC$FLAG_OPTIMIZE_PEEPHOLE) != 0) {
		if (JSC$verbose)
			JSC$message("jsc: optimize: peephole");

		for (item = JSC$asm_head; item != null; item = item.next) {
			/*
			 * Optimization for dup ... pop cases where pop removes the
			 * item duplicated by dup.
			 */
			if (item.next != null && item.next.type == JSC$OP_DUP) {
				var balance = 2;
				var found = false;
				var i1;

				for (i1 = item.next.next; i1 != null && i1.next != null; i1 = i1.next) {
					var i2 = i1.next;

					/*
					 * The lookup ends on branches, and on dup, throw,
					 * and try_pop operands.  We optimize on a basic
					 * block and we match the closest dup-pop pairs.
					 */
					if (JSC$asm_is_local_jump(i1)
						|| i1.type == JSC$OP_JSR || i1.type == JSC$OP_NEW
						|| i1.type == JSC$OP_CALL_METHOD || i1.type == JSC$OP_RETURN
						|| i1.type == JSC$ASM_SYMBOL || i1.type == JSC$ASM_LABEL
						|| i1.type == JSC$OP_DUP || i1.type == JSC$OP_TRY_POP
						|| i1.type == JSC$OP_THROW)
						break;

					if (i1.stack_delta) {
						balance += i1.stack_delta;
						if (balance <= 0)
							/* Going to negative.  Stop here. */
							break;
					}

					if (i2.type == JSC$OP_POP && balance == 1) {
						/* Found a matching pop. */
						found = true;
						i1.next = i2.next;
						break;
					}
				}

				if (found) {
					/* The dup can be removed. */
					item.next = item.next.next;
				}
			}

			/* Two instruction optimization (starting from item.next). */
			if (item.next != null && item.next.next != null) {
				var i1 = item.next;
				var i2 = i1.next;

				if (i1.type == JSC$OP_APOP && i2.type == JSC$OP_POP) {
					/*
					 * i1:	  apop n
					 * i2:	  pop	  ->  pop_n n + 1
					 */
					var i = new JSC$ASM_pop_n(i1.linenum, i1.value + 1);
					item.next = i;
					i.next = i2.next;
				}
			}
			if (item.next != null && item.next.next != null) {
				var i1 = item.next;
				var i2 = i1.next;

				if (i1.type == JSC$OP_CONST_TRUE
					&& (i2.type == JSC$OP_IFFALSE || i2.type == JSC$OP_IFFALSE_B)) {
					/*
					 * i1:	  const_true
					 * i2:	  iffalse{,_b}	  .LX => ---
					 */
					item.next = i2.next;
				}
			}
			if (item.next != null && item.next.next != null) {
				var i1 = item.next;
				var i2 = i1.next;

				if (i1.type == JSC$OP_CONST_FALSE
					&& (i2.type == JSC$OP_IFTRUE || i2.type == JSC$OP_IFTRUE_B)) {
					/*
					 * i1:	  const_false
					 * i2:	  iftrue{,_b} .LX => ---
					 */
					item.next = i2.next;
				}
			}
			if (item.next != null && item.next.next != null) {
				var i1 = item.next;
				var i2 = i1.next;

				if ((i1.type == JSC$OP_CONST_FALSE
					 && (i2.type == JSC$OP_IFFALSE || i2.type == JSC$OP_IFFALSE_B))
					|| (i1.type == JSC$OP_CONST_TRUE
						&& (i2.type == JSC$OP_IFTRUE || i2.type == JSC$OP_IFTRUE_B))) {
					/*
					 * i1:	  const_false
					 * i2:	  iffalse{,_b}	  .LX => jmp .LX
					 *
					 * i1:	  const_true
					 * i2:	  iftrue{,_b} .LX => jmp .LX
					 */
					var i = new JSC$ASM_jmp(i1.linenum, i2.value);
					item.next = i;
					i.next = i2.next;
				}
			}
		}
	}

	/* Jumps to jumps. */
	if ((flags & JSC$FLAG_OPTIMIZE_JUMPS) != 0) {
		if (JSC$verbose)
			JSC$message("jsc: optimize: jumps to jumps");
		for (item = JSC$asm_head; item != null; item = item.next)
			if (JSC$asm_is_local_jump(item)) {
				var i2;

				/* Operand's value is a label */
				i2 = JSC$asm_lookup_next_op(item.value);

				if (i2 != null && i2.type == JSC$OP_JMP)
					/* Ok, we can jump there directly. */
					item.value = i2.value;
			}
	}

	if ((flags & JSC$FLAG_OPTIMIZE_HEAVY) != 0)
		JSC$optimize_heavy();

	/*
	 * Optimizations for the size of the generated byte-code.  It isn't
	 * probably worth of doing these optimization for interactive
	 * scripts since these won't affect the speed of the execution.
	 * However, these optimizations make the byte-code files smaller so
	 * these are nice for batch-compiled files.
	 */
	if ((flags & JSC$FLAG_OPTIMIZE_BC_SIZE) != 0) {
		var delta = true;

		while (delta) {
			delta = false;

			/* Remove un-referenced labels. */

			if (JSC$verbose)
				JSC$message("jsc: optimize: removing un-referenced labels");

			/* First, make all labels unreferenced. */
			for (item = JSC$asm_head; item != null; item = item.next)
				if (item.type == JSC$ASM_LABEL)
					item.referenced = false;

			/* Second, mark all referenced labels. */
			for (item = JSC$asm_head; item != null; item = item.next)
				if (JSC$asm_is_local_jump(item))
					item.value.referenced = true;

			/* Third, remove all un-referenced labels. */
			for (item = JSC$asm_head; item != null; item = item.next)
				while (item.next != null && item.next.type == JSC$ASM_LABEL && !item.next.referenced
					   && item.next.next != null) {
					delta = true;
					item.next = item.next.next;
				}

			/* Dead code elimination. */
			if (JSC$verbose)
				JSC$message("jsc: optimize: dead code elimination");
			for (item = JSC$asm_head; item != null; item = item.next)
				if (item.type == JSC$OP_RETURN || item.type == JSC$OP_JMP)
					while (item.next != null && item.next.type != JSC$ASM_SYMBOL
						   && item.next.type != JSC$ASM_LABEL) {
						delta = true;
						item.next = item.next.next;
					}


			/* Simple peephole optimization. */
			if (JSC$verbose)
				JSC$message("jsc: optimize: peephole");
			for (item = JSC$asm_head; item != null; item = item.next) {
				/* Two instruction optimization (starting from item.next). */
				if (item.next != null && item.next.next != null) {
					var i1 = item.next;
					var i2 = i1.next;

					if (i1.type == JSC$OP_JMP && i2.type == JSC$ASM_LABEL && i1.value == i2) {
						/*
						 * i1:	  jmp .LX
						 * i2:	  .LX	  => .LX
						 */
						item.next = i2;
						delta = true;
					}
				}
			}
		}
	}
}


function JSC$optimize_heavy()
{
	if (JSC$verbose)
		JSC$message("jsc: optimize: liveness analyzing");

	/* First, set the prev pointers and zero usage flags. */
	var item, prev = null;

	for (item = JSC$asm_head; item != null; prev = item, item = item.next) {
		item.prev = prev;
		item.live_args = 0;
		item.live_locals = 0;
		item.live_used = false;
	}

	/* For each function. */
	var ftail, fhead;
	for (ftail = JSC$asm_tail; ftail != null; ftail = fhead.prev) {
		var change = true;

		/* While there is change in the liveness. */
		while (change) {
			change = false;

			for (fhead = ftail; fhead.type != JSC$ASM_SYMBOL; fhead = fhead.prev) {
				var floc, farg;

				if (fhead.next != null) {
					floc = fhead.next.live_locals;
					farg = fhead.next.live_args;
				} else
					floc = farg = 0;

				if (fhead.type == JSC$OP_LOAD_LOCAL && fhead.value < 32)
					floc |= (1 << fhead.value);

				if (fhead.type == JSC$OP_STORE_LOCAL && fhead.value < 32)
					floc &= ~(1 << fhead.value);

				if (fhead.type == JSC$OP_LOAD_ARG && fhead.value < 32)
					farg |= (1 << fhead.value);

				if (fhead.type == JSC$OP_STORE_ARG && fhead.value < 32)
					farg &= ~(1 << fhead.value);

				if (JSC$asm_is_local_jump(fhead)) {
					floc |= fhead.value.live_locals;
					fhead.value.live_used = true;
				}

				if (fhead.live_used && (fhead.live_locals != floc || fhead.live_args != farg))
					change = true;

				fhead.live_used = false;
				fhead.live_locals = floc;
				fhead.live_args = farg;
			}
		}
	}

	/*
	 * When we have the liveness analyzing performed, we can do some
	 * fancy optimizations.
	 */

	if (JSC$verbose)
		JSC$message("jsc: optimize: peephole");

	for (item = JSC$asm_head; item != null; item = item.next) {
		/* Three instruction optimization. */
		if (item.next != null && item.next.next != null && item.next.next.next != null) {
			var i1 = item.next;
			var i2 = i1.next;
			var i3 = i2.next;

			if (i1.type == JSC$OP_STORE_LOCAL && i2.type == JSC$OP_LOAD_LOCAL
				&& i1.value == i2.value && (i3.live_locals & (1 << i1.value)) == 0) {
				/*
				 * i1:	  store_local	  n
				 * i2:	  load_local  n
				 * i3:	  nnn (n not live)	  => nnn
				 */

				item.next = i3;
			}
		}
	}
}


function JSC$asm_finalize()
{
	var item;
	var offset = 0;

	for (item = JSC$asm_head; item != null; item = item.next) {
		item.offset = offset;
		offset += item.size;
	}
}


function JSC$ConstantReg()
{
}

function JSC$asm_genconstant(val)
{
	if (JSC$asm_known_constants == null)
		JSC$asm_known_constants = new JSC$ConstantReg();

	/* Lookup <val> from a list of known constants. */
	var id = JSC$asm_known_constants[val];
	if (typeof id == "number")
		return id;

	/* This is a new constant. */
	JSC$asm_constants = JSC$asm_constants.append(val);
	JSC$asm_known_constants[val] = JSC$asm_constcount;

	return JSC$asm_constcount++;
}

function JSC$asm_bytecode()
{
	var item;
	var symtab = new String("");
	var nsymtab_entries = 0;
	var code = new String("");
	var debug = new String("");
	var debug_last_linenum = 0;

	if (JSC$verbose)
		JSC$message("jsc: generating byte-code");

	if (JSC$generate_debug_info)
		/* Source file name. */
		debug = debug.append(String.pack("CN", JSC$DEBUG_FILENAME, JSC$filename.length)
							 + JSC$filename);

	JSC$asm_constants = new String("");

	for (item = JSC$asm_head; item != null; item = item.next) {
		if (item.type == JSC$ASM_SYMBOL) {
			symtab = symtab.append(item.value + String.pack("CN", 0, item.offset));
			nsymtab_entries++;
		} else if (item.type == JSC$ASM_LABEL);
		else {
			/* Real assembler operands. */

			if (JSC$generate_debug_info)
				if (item.linenum != debug_last_linenum) {
					debug =
						debug.append(String.
									 pack("CNN", JSC$DEBUG_LINENUMBER, item.offset + item.size,
										  item.linenum));
					debug_last_linenum = item.linenum;
				}
			if (item.size == 1)
				/* We handle these. */
				code = code.append(String.pack("C", item.type));
			else {
				/*
				 * All operands which take an argument, have a method to create
				 * the byte code entry for their argument.
				 */
				code = code.append(String.pack("C", item.type) + item.bytecode());
			}
		}
	}

	symtab = String.pack("N", nsymtab_entries) + symtab;

	if (JSC$verbose) {
		var msg = ("jsc: code=" + code.length.toString()
				   + ", constants=" + JSC$asm_constants.length.toString()
				   + ", symtab=" + symtab.length.toString());

		if (JSC$generate_debug_info)
			msg += ", debug=" + debug.length.toString();

		msg += (", headers=" + (32 + (JSC$generate_debug_info ? 8 : 0)).toString()
				+ ", total=" + (code.length + JSC$asm_constants.length + symtab.length +
								debug.length + 32 + (JSC$generate_debug_info ? 8 : 0)).toString());
		JSC$message(msg);
	}

	return (String.pack("NN", JSC$BC_MAGIC, 3 + (JSC$generate_debug_info ? 1 : 0))
			+ String.pack("NN", JSC$BC_SECT_CODE, code.length) + code + String.pack("NN",
																					JSC$BC_SECT_CONSTANTS,
																					JSC$asm_constants.
																					length)
			+ JSC$asm_constants + String.pack("NN", JSC$BC_SECT_SYMTAB,
											  symtab.length) + symtab +
			(JSC$generate_debug_info ? String.pack("NN", JSC$BC_SECT_DEBUG, debug.length) +
			 debug : ""));
}


/*
Local variables:
mode: c
End:
*/
